
extern crate winit;
extern crate vulkano as vk;
extern crate vulkano_win;

use winit::{ dpi::PhysicalSize};
use winit::{
    event::{Event, WindowEvent},
    event_loop::{ControlFlow, EventLoop},
    window::WindowBuilder,
};

use std::sync::Arc;

use vk::instance:: {
    Instance,
    InstanceExtensions,
    ApplicationInfo,
    Version,
};

use vk::instance::debug::{DebugCallback, MessageSeverity, MessageType};

use vk::device:: {
    physical::PhysicalDevice,
};


const WIDTH: u32 = 1024;
const HEIGHT: u32 = 768;

const VALIDATION_LAYERS: &[&str] = &[
    "VK_LAYER_KHRONOS_validation"
];

#[cfg(all(debug_assertions))]
const ENAVLE_VALIDATION_LAYERS: bool = true;
#[cfg(not(debug_assertions))]
const ENAVLE_VALIDATION_LAYERS: bool = false;

struct QueueFamilyIndices {
    graphics_family: i32,
}

impl QueueFamilyIndices {
    fn new() -> Self {
        Self {
            graphics_family: -1,
        }
    }

    fn is_complete(&self) -> bool {
        self.graphics_family >= 0
    }
}

#[allow(unused)]
struct Application {
    mInstance: Arc<Instance>,
    mDebugCallback: Option<DebugCallback>,
    mPhysicalDeviceIndex: usize, // can't store PhysicalDevice directly (lifetime issues)
}

impl Application {
    pub fn new() -> Self {
        let inst = Self::create_instance();
        let debug_callback = Self::set_debug_callback(&inst);
        let physical_device_index = Self::pick_physical_device(&inst);
        Self {
            mInstance:inst,
            mDebugCallback: debug_callback,
            mPhysicalDeviceIndex: physical_device_index,
        }
    }

    fn create_instance() -> Arc<Instance> {
        let supported_extensions = InstanceExtensions::supported_by_core()
            .expect("获取vulkan扩展失败！");
        println!("扩展有：{:?}", supported_extensions);
        
        let app_info = ApplicationInfo {
            application_name: Some("Hello vulkan".into()),
            application_version: Some(Version{major: 1, minor: 0, patch: 0}),
            engine_name: Some("DOO3D".into()),
            engine_version:Some(Version{major:1, minor:0, patch: 0}),
        };

        let required_extensions = Self::get_required_extensions();

        if ENAVLE_VALIDATION_LAYERS && Self::check_validation_support() {
            Instance::new(Some(&app_info),Version::V1_1, &required_extensions, VALIDATION_LAYERS.iter().cloned())
            .expect("创建带检验层的实例失败")
        } else {
            Instance::new(Some(&app_info),Version::V1_1, &required_extensions, None)
            .expect("创建实例失败")
        }
    }

    fn get_required_extensions() -> InstanceExtensions {
        let mut exts = vulkano_win::required_extensions();

        if ENAVLE_VALIDATION_LAYERS {
            // TODO!: this should be ext_debug_utils (_report is deprecated), but that doesn't exist yet in vulkano
            exts.ext_debug_utils = true;
        }

        exts
    }

    fn check_validation_support() -> bool {
        let layers: Vec<_> = vulkano::instance::layers_list().unwrap().map(|l| l.name().to_owned()).collect();
        println!("GPU layers:\n {:?}", layers);
        VALIDATION_LAYERS.iter()
            .all(|layer_name| layers.contains(&layer_name.to_string()))
    }

    fn pick_physical_device(instance: &Arc<Instance>) -> usize {
        let mut devices = PhysicalDevice::enumerate(&instance);

        let pos = devices.position(|device| Self::is_suitable_device(&device))
            .expect("没有合适的GPU硬件！");
        
        // all GPUS
        for device in devices {
            println!("可用的GPU：{}", device.properties().device_name);
        }

        pos
    }

    fn is_suitable_device(device: &PhysicalDevice) -> bool {
        let indices = Self::find_queue_families(&device);
        indices.is_complete()
    }

    fn find_queue_families(device: &PhysicalDevice) -> QueueFamilyIndices {
        let mut indices = QueueFamilyIndices::new();

         // TODO: replace index with id to simplify?
        for (i, queue_family) in device.queue_families().enumerate() {
            if queue_family.supports_graphics() {
                indices.graphics_family = i as i32;
            }
            if indices.is_complete() {
                break;
            }
        }

        indices
    }

    fn set_debug_callback(instance:&Arc<Instance>) -> Option<DebugCallback> {
        if !ENAVLE_VALIDATION_LAYERS {
            return None;
        }

        let severity = MessageSeverity {
            error: true,
            warning: true,
            information: true,
            verbose: true,
        };

        let ty = MessageType::all();
        DebugCallback::new(&instance, severity, ty, |msg| {
            let severity = if msg.severity.error {
                "error"
            } else if msg.severity.warning {
                "warning"
            } else if msg.severity.information {
                "information"
            } else if msg.severity.verbose {
                "verbose"
            } else {
                panic!("no-impl");
            };
    
            let ty = if msg.ty.general {
                "general"
            } else if msg.ty.validation {
                "validation"
            } else if msg.ty.performance {
                "performance"
            } else {
                panic!("no-impl");
            };
    
            println!(
                "{} {} {}: {}",
                msg.layer_prefix.unwrap_or("unknown"),
                ty,
                severity,
                msg.description
            );    
        })
        .ok()
    }

    pub fn run(&mut self) {
        let event_loop = EventLoop::new();
        let _window = WindowBuilder::new()
            .with_title("03 物理设备与队列族")
            // .with_resizable(false) // false：窗口不可缩放
            .with_inner_size(PhysicalSize::new(WIDTH, HEIGHT))
            .build(&event_loop)
            .unwrap();

        event_loop.run(move |event, _, control_flow |{
            *control_flow = ControlFlow::Poll;
            *control_flow = ControlFlow::Wait;

            match event {
                Event::WindowEvent {
                    event: WindowEvent::CloseRequested,
                    ..
                } => {
                    println!("窗口关闭！");
                    *control_flow = ControlFlow::Exit;
                },
                Event::WindowEvent {
                    event: WindowEvent::Resized(_),
                    ..
                } => {
                    println!("window resize");
                },
                Event::MainEventsCleared => {
                    // Application update code.

                    // Queue a RedrawRequested event.
                    //
                    // You only need to call this if you've determined that you need to redraw, in
                    // applications which do not always need to. Applications that redraw continuously
                    // can just render here instead.

                    _window.request_redraw();
                },
                Event::RedrawRequested(_) => {
                    // Redraw the application.
                    //
                    // It's preferable for applications that do not render continuously to render in
                    // this event rather than in MainEventsCleared, since rendering in here allows
                    // the program to gracefully handle redraws requested by the OS.
                },
                _ =>()
            }
        });
    }
}

fn main() {
    let mut app = Application::new();
    app.run();
}